﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml.Linq;
using System.IO;
using System.Configuration;

namespace Telmex
{
    /// <summary>
    /// Exposes the available services of the Telmex API.
    /// </summary>
    public class TelmexService
    {
        /// <summary>
        /// Base address of the location of the Telmex API.
        /// </summary>
        private const string ApiBaseUrl = "http://simon.telmex.net.ar/";

        /// <summary>
        /// Relative address for the Login action.
        /// </summary>
        private const string ApiLoginUrl = "index.php";

        /// <summary>
        /// Relative address for the Create Room action.
        /// </summary>
        private const string ApiCreateRoomUrl = "meeting/admin3.php";

        /// <summary>
        /// Stores the auth cookie.
        /// </summary>
        CookieContainer cookieJar = new CookieContainer();

        /// <summary>
        /// Initiates a session with Telmex using the configured default credentials.
        /// </summary>
        /// <returns></returns>
        public bool Login()
        {
            // Get the credentials from the config settings
            var section = ConfigurationManager.GetSection("telmex") as TelmexConfigurationSection;

            return Login(
                section.ApiUsername,
                section.ApiSecret);
        }

        /// <summary>
        /// Initiates a session with Telmex with the given credentials.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="secret"></param>
        /// <returns></returns>
        public bool Login(string username, string secret)
        {
            if (String.IsNullOrWhiteSpace(username))
                throw new ArgumentException("username");

            if (String.IsNullOrWhiteSpace(secret))
                throw new ArgumentException("secret");

            // Setup the api request
            HttpWebRequest request = HttpWebRequest.Create(ApiBaseUrl + ApiLoginUrl) as HttpWebRequest;
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("user", username);
            parameters.Add("passwd", secret);
            parameters.Add("Submit", "Aceptar");

            // Set the cookie container; in this way
            // we can reuse the cookies for further calls
            request.CookieContainer = cookieJar;

            // Post
            HttpWebResponse response = request.Post(parameters);

            // Free resources
            response.Close();

            // Check for the cookie
            var loggedIn = request.CookieContainer.Count < 1;

            return loggedIn;
        }

        /// <summary>
        /// Tells if the service already logged in Telmex.
        /// </summary>
        public bool IsLoggedIn
        {
            get
            {
                // If there is a auth cookie in the jar, the user is logged in.
                return cookieJar.Count > 0;
            }
        }

        /// <summary>
        /// Creates a conference in Telmex.
        /// </summary>
        /// <param name="conference">The conference to create.</param>
        /// <returns>The just created <see cref="Telmex.Conference"/>.</returns>
        public Conference CreateConference(Conference conference)
        {
            // Get the responsible defaults from the config settings
            var section = ConfigurationManager.GetSection("telmex") as TelmexConfigurationSection;

            return CreateConference(
                conference.StartsOn, 
                Convert.ToInt32(conference.Duration.TotalMinutes), 
                conference.Participants, 
                conference.Name,
                section.DefaultResponsibleId,
                section.DefaultResponsibleEmail);
        }

        /// <summary>
        /// Creates a conference in Telmex.
        /// </summary>
        /// <param name="start">The starting date (year, month, day) and starting time (hour, minutes).</param>
        /// <param name="duration">The duration of the room, in minutes. The room is allocated in spans of 30 minutes, rounded up.</param>
        /// <param name="participants">The maximum number of concurrent participants.</param>
        /// <param name="description">A friendly description, used in the admin tool.</param>
        /// <param name="responsibleId">The Telmex identifier of the responsible.</param>
        /// <param name="responsibleEmail">The email of the responsible.</param>
        /// <returns>The just created <see cref="Telmex.Conference"/>.</returns>
        public Conference CreateConference(DateTime start, int duration, int participants, string description, string responsibleId, string responsibleEmail)
        {
            if (duration < 1)
                throw new ArgumentOutOfRangeException("duration");

            if (participants < 2)
                throw new ArgumentOutOfRangeException("participants");

            if (String.IsNullOrWhiteSpace("description"))
                throw new ArgumentException("description");

            if (!this.IsLoggedIn)
                throw new InvalidOperationException("Service not logged in.");

            // Round up the duration
            duration = duration - (duration % 30) + ((duration % 30) > 0 ? 30 : 0);

            // Setup the conference request
            HttpWebRequest conferenceRequest = HttpWebRequest.Create(ApiBaseUrl + ApiCreateRoomUrl) as HttpWebRequest;
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("descripcion_c", description);
            parameters.Add("x_dia_c", start.Day.ToString());
            parameters.Add("x_mes_c", start.Month.ToString());
            parameters.Add("x_ano_c", start.Year.ToString());
            parameters.Add("x_hora_c", start.ToString("HH:mm"));
            parameters.Add("participantes_c", participants.ToString());
            parameters.Add("duracion_c", new TimeSpan(0, duration, 0).ToString(@"hh\:mm"));
            parameters.Add("responsable_c", responsibleId);
            parameters.Add("Submit", "Crear");

            // Reuse the login cookie
            conferenceRequest.CookieContainer = cookieJar;
            
            // Post
            HttpWebResponse conferenceResponse = conferenceRequest.Post(parameters);

            // Read the response
            string conferenceRawData = conferenceResponse.ReadString();

            // Get the pseudo-captcha value (we cannot read it as xml because is just loosy html)
            string captchaValue = conferenceRawData.Substring(conferenceRawData.IndexOf("clave_temp") + 21, 6);

            // Setup the confirmation request
            HttpWebRequest confirmation = HttpWebRequest.Create(ApiBaseUrl + ApiCreateRoomUrl) as HttpWebRequest;
            parameters.Add("responsable_mail", responsibleEmail);
            parameters.Remove("Submit");
            parameters.Add("Submit", "Validar");
            parameters.Add("validacion", captchaValue);
            parameters.Add("clave_temp", captchaValue);

            // Reuse the login cookie
            confirmation.CookieContainer = cookieJar;

            var confirmationResponse = confirmation.Post(parameters);

            // Read the response
            string confirmationRawData = confirmationResponse.ReadString();

            // Set-up the result
            var conference = new Conference();

            // Again... loosy html. I hate myself for doing this,
            // but there is no option.
            conference.Room = confirmationRawData.Substring(confirmationRawData.IndexOf("El numero de sala correspondiente es:") + 37, 3);
            conference.Pin = confirmationRawData.Substring(confirmationRawData.IndexOf("La clave para ingresar a la conferencia es:") + 43, 5);
            conference.LocalCallPhone = confirmationRawData.Substring(confirmationRawData.IndexOf("El numero de acceso al servicio es:") + 36, 12);
            conference.RemoteCallPhone = confirmationRawData.Substring(confirmationRawData.IndexOf("(ARGENTINA) o") + 14, 14);
            conference.Duration = new TimeSpan(0, duration, 0);
            conference.Name = description;
            conference.Participants = participants;
            conference.StartsOn = start;
            
            return conference;
        }
    }
}
